from fastapi import FastAPI, Request, Depends
from fastapi.responses import JSONResponse
from fastapi.responses import PlainTextResponse
import time
from contextlib import asynccontextmanager
from .routes_qwen import router as qwen_router
from .routes_perception import router as perception_router
from .routes_decision import router as decision_router
from .routes_content import router as content_router
from .routes_announcements import router as announcements_router
from .routes_config import router as config_router
from .routes_cfg_reload import router as cfg_reload_router
from .routes_ops import router as ops_router
from .routes_events import router as events_router
from .routes_data import router as data_router
from intelli_port.commons.config import load_settings
from intelli_port.commons.middleware import JavaSignatureMiddleware, CorrelationIdMiddleware, IdempotencyMiddleware
from intelli_port.commons.logging import json_logger, emit_to_elastic
from intelli_port.commons.observability import http_requests_total, http_request_latency_ms, service_available
from intelli_port.model import provider_http as qwen_http_provider
from intelli_port.model.qwen import provider_vllm as qwen_vllm_provider
from intelli_port.data_clients.redis import get_redis
from intelli_port.data_clients.milvus import get_milvus
from intelli_port.data_clients.neo4j import get_neo4j
from intelli_port.data_clients.mysql import get_mysql
from intelli_port.data_clients.mysql import get_mysql_read
from intelli_port.auth.deps import ops_protect, config_protect
from intelli_port.robot_control.retry_worker import start_robot_retry_worker


def create_app() -> FastAPI:
    @asynccontextmanager
    async def lifespan(app: FastAPI):
        service_available.set(1)
        try:
            r = get_redis()
            app.state.redis = r
        except Exception:
            pass
        try:
            m = get_milvus()
            app.state.milvus = m
        except Exception:
            pass
        try:
            n = get_neo4j()
            app.state.neo4j = n
        except Exception:
            pass
        try:
            app.state.mysql = get_mysql()
        except Exception:
            pass
        try:
            app.state.mysql_read = get_mysql_read()
        except Exception:
            pass
        # Start background workers
        try:
            import asyncio as _asyncio
            app.state.robot_retry_task = _asyncio.create_task(start_robot_retry_worker(app))
        except Exception:
            pass
        yield
        service_available.set(0)
        try:
            import asyncio as _asyncio
            t = getattr(app.state, "robot_retry_task", None)
            if t:
                t.cancel()
        except Exception:
            pass
        try:
            if getattr(qwen_http_provider, "_client", None):
                await qwen_http_provider._client.aclose()
        except Exception:
            pass
        try:
            if getattr(qwen_vllm_provider, "_client", None):
                await qwen_vllm_provider._client.aclose()
        except Exception:
            pass
        try:
            m = getattr(app.state, "milvus", None)
            if m and hasattr(m, "close"):
                m.close()
        except Exception:
            pass
        try:
            n = getattr(app.state, "neo4j", None)
            if n:
                n.close()
        except Exception:
            pass
        try:
            mysql_pool = getattr(app.state, "mysql", None)
            if mysql_pool:
                mysql_pool.close()
        except Exception:
            pass
        try:
            mysql_pool_r = getattr(app.state, "mysql_read", None)
            if mysql_pool_r:
                mysql_pool_r.close()
        except Exception:
            pass

    app = FastAPI(title="Intelli Port API", lifespan=lifespan)
    s = load_settings()
    app.add_middleware(CorrelationIdMiddleware)
    app.add_middleware(JavaSignatureMiddleware, secret=s.java_signature_secret)
    app.add_middleware(IdempotencyMiddleware)

    logger = json_logger()
    tracer = None
    try:
        import importlib
        _otel = importlib.import_module("opentelemetry.trace")
        tracer = _otel.get_tracer("intelli-port")
    except Exception:
        tracer = None

    @app.middleware("http")
    async def metrics_middleware(request: Request, call_next):
        t0 = time.time()
        if tracer:
            with tracer.start_as_current_span("http.request") as span:
                span.set_attribute("http.method", request.method)
                span.set_attribute("http.path", request.url.path)
                resp = await call_next(request)
        else:
            resp = await call_next(request)
        path = request.url.path
        method = request.method
        status = resp.status_code
        http_requests_total.labels(path=path, method=method, status=str(status)).inc()
        http_request_latency_ms.labels(path=path, method=method).observe(max(0, (time.time() - t0) * 1000))
        payload = {"req_id": request.headers.get("x-request-id", ""), "corr_id": request.headers.get("x-correlation-id", ""), "service": "api"}
        logger.info("http.request", extra=payload)
        emit_to_elastic({"event": "http.request", **payload})
        return resp

    app.include_router(qwen_router, prefix="/qwen", tags=["qwen"])
    app.include_router(perception_router, prefix="/perception", tags=["perception"])
    app.include_router(decision_router, prefix="/decision", tags=["decision"])
    app.include_router(content_router, prefix="/content", tags=["content"])
    app.include_router(announcements_router, prefix="/announcements", tags=["announcements"])
    app.include_router(config_router, prefix="/config", tags=["config"], dependencies=[Depends(config_protect)])
    app.include_router(cfg_reload_router, prefix="/config", tags=["config"], dependencies=[Depends(config_protect)])
    app.include_router(ops_router, prefix="/ops", tags=["ops"], dependencies=[Depends(ops_protect)])
    app.include_router(events_router, prefix="/events", tags=["events"])
    app.include_router(data_router, prefix="/data", tags=["data"])

    @app.get("/health")
    async def health():
        return JSONResponse({"ok": True})

    @app.get("/metrics")
    async def metrics():
        try:
            from prometheus_client import generate_latest
        except Exception:
            return PlainTextResponse("", media_type="text/plain; version=0.0.4")
        return PlainTextResponse(generate_latest(), media_type="text/plain; version=0.0.4")

    @app.get("/model/paths")
    async def model_paths():
        s = load_settings()
        return {
            "qwen_sft_data_dir": s.qwen_sft_data_dir,
            "qwen_output_dir": s.qwen_output_dir,
            "asr_finetune_data_dir": s.asr_finetune_data_dir,
            "tts_finetune_data_dir": s.tts_finetune_data_dir,
            "asr_output_dir": s.asr_output_dir,
            "tts_output_dir": s.tts_output_dir,
        }

    return app